package restful;

import static java.text.MessageFormat.*;

import java.net.*;
import java.util.*;
import java.util.regex.*;

public class UriExpression {

	private Scanner scanner;

	private StringBuilder regex = new StringBuilder();

	public UriExpression(String uriTemplate) {
		append(verb(uriTemplate));
		String a = ignoreVerb(uriTemplate);
		R(a);
	}

	private String verb(String uriTemplate) {
		if(isPost(uriTemplate)) return "POST ";
		if(isGet(uriTemplate)) return "GET ";
		if(isPut(uriTemplate)) return "PUT ";
		if(isDelete(uriTemplate)) return "DELETE ";
		return "";
	}

	private boolean isDelete(String uriTemplate) {
		return uriTemplate.startsWith("DELETE");
	}

	private boolean isPut(String uriTemplate) {
		return uriTemplate.startsWith("PUT");
	}

	private boolean isGet(String uriTemplate) {
		return uriTemplate.startsWith("GET");
	}

	private boolean isPost(String uriTemplate) {
		return uriTemplate.startsWith("POST");
	}

	private StringBuilder constant(StringBuilder stringBuilder, String token) {
		return stringBuilder.append(encode(token));
	}

	private boolean containsRegex(String token) {
		return token.contains(":");
	}

	private String ignoreVerb(String uri) {
		uri = uri.replaceAll("^POST ?", "");
		uri = uri.replaceAll("^GET ?", "");
		uri = uri.replaceAll("^PUT ?", "");
		uri = uri.replaceAll("^DELETE ?", "");
		return uri;
	}

	private boolean isVariable(String token) {
		return token.startsWith("{");
	}

	private Map<String, Object> matching(Collection<String> parameterNames, Matcher matcher) {
		Map<String, Object> parameters = new LinkedHashMap<String, Object>();
		int i = 1;
		for (String parameterName: parameterNames) {
			parameters.put(Matchers.nameOf(parameterName), matcher.group(i++));
		}
		return parameters;
	}

	public Map<String, Object> paramters(Collection<String> parameterNames, String uri) {
		Matcher matcher = Pattern.compile(regexWithoutVerb()).matcher(ignoreVerb(uri));
		if (!matcher.matches()) throw new RuntimeException(uri + " not matches");
		return matching(parameterNames, matcher);
	}

	private String extractRegex(String token) {
		return removeBrackets(token).replaceAll("^.*:", "");
	}

	public String regexWithoutVerb() {
		return format("^{0}$", regex.toString());
	}

	private String removeBrackets(String token) {
		return token.replaceAll("\\{", "").replaceAll("\\}", "");
	}

	@Override
	public String toString() {
		return regex.toString();
	}

	private void R(String uriTemplate) {
		if(uriTemplate.startsWith("/") && !"/".equals(uriTemplate)) {
			append("/");
		}
		scanner = new Scanner(uriTemplate).useDelimiter("/");
		while (scanner.hasNext()) {
			appendToken(scanner);
			appendSeparator();
		}
		if(uriTemplate.endsWith("/")) {
			append("(/.*)?");
		}
	}

	private void appendToken(Scanner scanner) {
		String token = scanner.next();
		if (isVariable(token)) {
			variable(regex, token);
		} else {
			constant(regex, token);
		}
	}

	private void appendSeparator() {
		if(scanner.hasNext()) {
			append("/");
		}
	}

	private void append(String str) {
		regex.append(str);
	}

	private String encode(String token) {
		if("/".equals(token)) return token;
		try {
			return URLEncoder.encode(token, "UTF-8");
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void variable(StringBuilder result, String token) {
		if (containsRegex(token)) {
			result.append(format("({0})", extractRegex(token)));
		} else {
			result.append(format("([^/]+?)"));
		}
	}

}